# -!-coding:utf-8-!-
# @Time: 2022/3/6
# @Dev: TJ

import gc
import time
import random
from pathlib import Path
from loguru import logger
from lxml import etree

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver import ActionChains
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

from src.sub_xpath.sub_xpath import xpath_string
from src.sub_faker.sub_faker_ua import SubFakerUA

fake = SubFakerUA()


class ProxyChannel:
    """ 代理 """
    CLOSE = None
    TX_CN7009 = 'http://10.0.3.43:7009'
    TX_CN5999 = 'socks5://10.0.3.43:5999'

    @staticmethod
    def random_proxy() -> str:
        return f'http://10.0.3.43:{random.randint(8001, 8016)}'


class SubChrome(Chrome, ProxyChannel):
    def __init__(
            self,
            proxy=ProxyChannel.CLOSE,  # 代理
            mobile=False,  # 手机模式
            img_enabled=False,  # 显示图片
            verify_enabled=False  # SSL验证
    ):
        super().__init__(
            desired_capabilities=self.chrome_capabilities(mobile),
            options=self.options(
                proxy=proxy, mobile=mobile,
                img_enabled=img_enabled,
                verify_enabled=verify_enabled)
        )

        # self.maximize_window()  # 窗口最大化
        self.set_page_load_timeout(31)  # 页面加载时间
        self.hide_window_navigator_webdriver()

    @staticmethod
    def options(proxy, mobile, img_enabled, verify_enabled):
        """ 浏览器初始化 """
        options = ChromeOptions()
        options.headless = True  # 无界模式
        # options.add_argument('--headless')                  # 无界模式
        options.add_argument('--lang=zh-CN,zh;en-US,en;')     # 设置语言支持
        options.add_argument('--charset=UTF-8')               # 编码格式
        options.add_argument('--no-sandbox')                # 取消沙盒模式
        options.add_argument('--disable-dev-shm-usage')
        options.add_argument('--disable-gpu')               # 关闭渲染
        options.add_argument('--window-size=1920,1080')       # 网页分辨率，避免点不到的情况
        options.add_argument('--incognito')                 # 无痕模式
        options.add_argument(f'--user-agent={fake.chrome()}')  # 伪造 UA
        options.add_experimental_option(
            'excludeSwitches', ['enable-automation'])  # 修改 window.navigator.webdriver 值的 chrome 启动配置代码

        if verify_enabled is False:
            options.add_argument('--ignore-certificate-errors')  # 关闭证书验证

        if img_enabled is False:
            options.add_argument('blink-settings=imagesEnabled=false')  # 不加载图片

        if proxy:
            options.add_argument('--proxy-server=' + proxy)  # 设置代理（无认证）

        if mobile:
            options.add_experimental_option(
                "mobileEmulation", {"deviceName": "Galaxy S5"})  # 设置手机机型，根据 Chrome 支持的机型名称

        print(options.arguments)
        return options

    @staticmethod
    def chrome_capabilities(mobile):
        """ 开启手机模式 """
        if mobile is False:
            return None

        capabilities = DesiredCapabilities.CHROME
        capabilities['loggingPrefs'] = {'browser': 'ALL'}
        return capabilities

    def hide_window_navigator_webdriver(self):
        """ 隐藏 window.navigator.webdriver 的值 """
        self.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
            "source": """
                Object.defineProperty(navigator, 'webdriver', {
                    get: () => undefined
                })
            """
        })

    def get(self, url) -> bool:
        try:
            for _ in range(3):
                super().get(url)

                if 'HTTP ERROR 503' in self.page_source:
                    logger.warning('HTTP ERROR 503')
                    time.sleep(1)
                    continue

                if self.title.find('江苏反诈公益宣传') >= 0:
                    logger.warning('江苏反诈公益宣传')
                    time.sleep(1)
                    continue

                return True

        except Exception as exception:
            logger.error(f'Exception: {exception} | Why?')

        return False

    def wait_element(self, element_type: str, element_name: str):
        """ 等待元素可见 """
        try:
            wait = WebDriverWait(self, 30)  # 设置等待时间
            wait.until(EC.visibility_of_element_located((
                element_type, element_name)))  # 当出现该条件，终止等待
            return True

        except Exception as exception:
            logger.error(exception)

        return False

    def slider_verify(self, slider_element, max_offset, speeds: tuple = (10, 20)):
        """ 滑块验证 """
        if not slider_element:
            return

        max_offset = max_offset - slider_element.size['width'] + 5

        def get_track(distance):
            """ 模拟用户行为 """
            track = []
            current = 0
            mid = distance * 3 / 4
            t = random.randint(*speeds) / 10  # 值越大，速度越快，偏移像素差距越大
            v = 0
            while current < distance:
                if current < mid:
                    a = 2
                else:
                    a = -3
                v = v + a * t
                move = v * t + 1 / 2 * a * t * t
                current += move
                track.append(round(move))
            return track

        self.execute_script("arguments[0].scrollIntoView();", slider_element)
        ActionChains(self).click_and_hold(slider_element).perform()  # 按住鼠标
        # 拖动
        for offset in get_track(max_offset):
            ActionChains(self).move_by_offset(xoffset=offset, yoffset=0).perform()  # 拖动鼠标
        ActionChains(self).release().perform()  # 释放鼠标

    def screenshot_as_file(self, file_name=None, path='./screenshot_png') -> str:
        """ 网页截图保存本地 """
        if not file_name:
            url: str = self.current_url
            file_name = url.split('/')[-1]

        if not file_name:
            file_name = '%06d' % random.randint(0, 999999)

        file_name = '.'.join([file_name, 'png'])

        path = Path(path, file_name)
        path.parent.mkdir(parents=True, exist_ok=True)

        self.get_screenshot_as_file(str(path.absolute()))
        return str(path.absolute())

    def quit(self):
        """ 退出 driver """
        logger.info(f'quit: {self}')
        super().close()
        super().quit()

    def stop_client(self):
        """ 停止客户端 """
        logger.info(f'stop_client: {self}')
        super().stop_client()
        gc.collect()  # 回收资源


if __name__ == '__main__':
    while True:
        try:
            driver = SubChrome(proxy=ProxyChannel.TX_CN7009)
            driver.get('http://myip.ipip.net/')
            html = etree.HTML(driver.page_source)
            print(xpath_string(html, '.', scrapy=False))
            print('截图路径:', driver.screenshot_as_file())
            time.sleep(3)
            driver.quit()
            del driver

        except Exception as e:
            print(e)
            break
